<?php

namespace aiwei;

/**
 * 日期时间处理类
 */
class Date
{
    const YEAR = 31536000;
    const MONTH = 2592000;
    const WEEK = 604800;
    const DAY = 86400;
    const HOUR = 3600;
    const MINUTE = 60;

    /**
     * 计算两个时区间相差的时长,单位为秒
     *
     * $seconds = self::offset('America/Chicago', 'GMT');
     *
     * [!!] A list of time zones that PHP supports can be found at
     * <http://php.net/timezones>.
     *
     * @param string $remote timezone that to find the offset of
     * @param string $local timezone used as the baseline
     * @param mixed $now UNIX timestamp or date string
     * @return  integer
     */
    public static function offset($remote, $local = null, $now = null)
    {
        if ($local === null) {
            // Use the default timezone
            $local = date_default_timezone_get();
        }
        if (is_int($now)) {
            // Convert the timestamp into a string
            $now = date(DateTime::RFC2822, $now);
        }
        // Create timezone objects
        $zone_remote = new DateTimeZone($remote);
        $zone_local = new DateTimeZone($local);
        // Create date objects from timezones
        $time_remote = new DateTime($now, $zone_remote);
        $time_local = new DateTime($now, $zone_local);
        // Find the offset
        $offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
        return $offset;
    }

    /**
     * 计算两个时间戳之间相差的时间
     *
     * $span = self::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
     * $span = self::span(60, 182, 'minutes'); // 2
     *
     * @param int $remote timestamp to find the span of
     * @param int $local timestamp to use as the baseline
     * @param string $output formatting string
     * @return  string   when only a single output is requested
     * @return  array    associative list of all outputs requested
     * @from https://github.com/kohana/ohanzee-helpers/blob/master/src/Date.php
     */
    public static function span($remote, $local = null, $output = 'years,months,weeks,days,hours,minutes,seconds')
    {
        // Normalize output
        $output = trim(strtolower((string)$output));
        if (!$output) {
            // Invalid output
            return false;
        }
        // Array with the output formats
        $output = preg_split('/[^a-z]+/', $output);
        // Convert the list of outputs to an associative array
        $output = array_combine($output, array_fill(0, count($output), 0));
        // Make the output values into keys
        extract(array_flip($output), EXTR_SKIP);
        if ($local === null) {
            // Calculate the span from the current time
            $local = time();
        }
        // Calculate timespan (seconds)
        $timespan = abs($remote - $local);
        if (isset($output['years'])) {
            $timespan -= self::YEAR * ($output['years'] = (int)floor($timespan / self::YEAR));
        }
        if (isset($output['months'])) {
            $timespan -= self::MONTH * ($output['months'] = (int)floor($timespan / self::MONTH));
        }
        if (isset($output['weeks'])) {
            $timespan -= self::WEEK * ($output['weeks'] = (int)floor($timespan / self::WEEK));
        }
        if (isset($output['days'])) {
            $timespan -= self::DAY * ($output['days'] = (int)floor($timespan / self::DAY));
        }
        if (isset($output['hours'])) {
            $timespan -= self::HOUR * ($output['hours'] = (int)floor($timespan / self::HOUR));
        }
        if (isset($output['minutes'])) {
            $timespan -= self::MINUTE * ($output['minutes'] = (int)floor($timespan / self::MINUTE));
        }
        // Seconds ago, 1
        if (isset($output['seconds'])) {
            $output['seconds'] = $timespan;
        }
        if (count($output) === 1) {
            // Only a single output was requested, return it
            return array_pop($output);
        }
        // Return array
        return $output;
    }

    /**
     * 格式化 UNIX 时间戳为人易读的字符串
     *
     * @param int    Unix 时间戳
     * @param mixed $local 本地时间
     *
     * @return    string    格式化的日期字符串
     */
    public static function human($remote, $local = null)
    {
        $timediff = (is_null($local) || $local ? time() : $local) - $remote;
        $chunks = [
            [60 * 60 * 24 * 365, 'year'],
            [60 * 60 * 24 * 30, 'month'],
            [60 * 60 * 24 * 7, 'week'],
            [60 * 60 * 24, 'day'],
            [60 * 60, 'hour'],
            [60, 'minute'],
            [1, 'second']
        ];

        for ($i = 0, $j = count($chunks); $i < $j; $i++) {
            $seconds = $chunks[$i][0];
            $name = $chunks[$i][1];
            if (($count = floor($timediff / $seconds)) != 0) {
                break;
            }
        }
        return __("%d {$name}%s ago", $count, ($count > 1 ? 's' : ''));
    }

    /**
     * 获取一个基于时间偏移的Unix时间戳
     *
     * @param string $type 时间类型，默认为day，可选minute,hour,day,week,month,quarter,year
     * @param int $offset 时间偏移量 默认为0，正数表示当前type之后，负数表示当前type之前
     * @param string $position 时间的开始或结束，默认为begin，可选前(begin,start,first,front)，end
     * @param int $year 基准年，默认为null，即以当前年为基准
     * @param int $month 基准月，默认为null，即以当前月为基准
     * @param int $day 基准天，默认为null，即以当前天为基准
     * @param int $hour 基准小时，默认为null，即以当前年小时基准
     * @param int $minute 基准分钟，默认为null，即以当前分钟为基准
     * @return int 处理后的Unix时间戳
     */
    public static function unixtime($type = 'day', $offset = 0, $position = 'begin', $year = null, $month = null, $day = null, $hour = null, $minute = null)
    {
        $year = is_null($year) ? date('Y') : $year;
        $month = is_null($month) ? date('m') : $month;
        $day = is_null($day) ? date('d') : $day;
        $hour = is_null($hour) ? date('H') : $hour;
        $minute = is_null($minute) ? date('i') : $minute;
        $position = in_array($position, ['begin', 'start', 'first', 'front']);

        switch ($type) {
            case 'minute':
                $time = $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year);
                break;
            case 'hour':
                $time = $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year);
                break;
            case 'day':
                $time = $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year);
                break;
            case 'week':
                $time = $position ?
                    mktime(0, 0, 0, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 1 - 7 * (-$offset), $year) :
                    mktime(23, 59, 59, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 7 - 7 * (-$offset), $year);
                break;
            case 'month':
                $time = $position ? mktime(0, 0, 0, $month + $offset, 1, $year) : mktime(23, 59, 59, $month + $offset, cal_days_in_month(CAL_GREGORIAN, $month + $offset, $year), $year);
                break;
            case 'quarter':
                $time = $position ?
                    mktime(0, 0, 0, 1 + ((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) - 1) * 3, 1, $year) :
                    mktime(23, 59, 59, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3,
                        cal_days_in_month(CAL_GREGORIAN, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, $year), $year);
                break;
            case 'year':
                $time = $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset);
                break;
            default:
                $time = mktime($hour, $minute, 0, $month, $day, $year);
                break;
        }
        return $time;
    }


    /**
     * 距今 时间戳人性化转化
     * @param $time  int //时间戳
     * @return string
     */
    public static function timeTran(int $time): ?string
    {
        $t = time() - $time;
        $f = [
            '31536000' => '年',
            '2592000' => '个月',
            '604800' => '周',
            '86400' => '天',
            '3600' => '小时',
            '60' => '分钟',
            '1' => '秒'
        ];
        foreach ($f as $k => $v) {
            if (0 != $c = floor($t / (int)$k)) {
                return $c . $v . '前';
            }
        }
        return '';
    }

    /**
     * 获取今年/本月/今日 或 明年/下月/明天 的时间戳
     * $Ymd = Y 年
     * $Ymd = m 月
     * $Ymd = d 日
     * $Ymd = NULL 当前时间戳
     * $xia = true 是否取下次开始时间戳：取下年开始时间戳 或者下月开始时间戳  或者明日开始时间戳
     */
    public static function getTime($Ymd = null, $xia = false)
    {
        if ($Ymd === 'Y' && $xia == true) {
            //取下个年度开始时间戳
            return strtotime((date('Y', time()) + 1) . '-01-01 00:00:00');
        }

        if ($Ymd === 'Y') {
            //取本年度开始时间戳
            return strtotime(date('Y', time()) . '-01-01 00:00:00');
        }

        if ($Ymd === 'm' && $xia == true) {
            //取下个月度开始时间戳
            $xiayue_nianfen = date('Y', time());
            $xiayue_yuefen = date('m', time());
            if ($xiayue_yuefen == 12) {
                $xiayue_nianfen += 1;    //如果月份等于12月，那么下月年份+1
                $xiayue_yuefen = 1;    //如果月份等于12月，那么下月月份=1月
            } else {
                $xiayue_yuefen += 1;    //如果月份不是12月，那么在当前月份上+1
            }
            return strtotime($xiayue_nianfen . '-' . $xiayue_yuefen . '-01 00:00:00');
        }

        if ($Ymd === 'm') {
            //取本月度开始时间戳
            return strtotime(date('Y-m', time()) . '-01 00:00:00');
        }

        if ($Ymd === 'd' && $xia === true) {
            //取明日开始时间戳
            return strtotime(date('Y-m-d', time()) . ' 00:00:00') + 86400;
        }

        if ($Ymd === 'd') {
            //取今日开始时间戳
            return strtotime(date('Y-m-d', time()) . ' 00:00:00');
        }

        //取当前时间戳
        return time();
    }

    /**
     * 计算两个时间戳之间相差的时间
     *
     * @param $begin_time
     * @param $end_time
     * @return array
     */
    public static function timeDiff($begin_time, $end_time)
    {
        if ($begin_time < $end_time) {
            $starttime = $begin_time;
            $endtime = $end_time;
        } else {
            $starttime = $end_time;
            $endtime = $begin_time;
        }
        //计算天数
        $timediff = $endtime - $starttime;
        $days = (int)($timediff / 86400);
        //计算小时数
        $remain = $timediff % 86400;
        $hours = (int)($remain / 3600);
        //计算分钟数
        $remain = $remain % 3600;
        $mins = (int)($remain / 60);
        //计算秒数
        $secs = $remain % 60;
        $res = ["day" => $days, "hour" => $hours, "min" => $mins, "sec" => $secs];
        return $res;
    }

    /**
     * 格式化时间
     * @param $timediff
     * @return string
     */
    public static function getTimeHour($timediff)
    {

        //计算小时数
        $remain = $timediff % 86400;
        $hours = (int)($remain / 3600);
        //计算分钟数
        $remain %= 3600;
        $mins = (int)($remain / 60);
        //计算秒数
        $secs = $remain % 60;
        //$res = array("day" => $days,"hour" => $hours,"min" => $mins,"sec" => $secs);
        return "{$hours}时{$mins}分{$secs}秒";
    }

    /**
     * 返回一定范围的全部日期
     * @param $start string  //开始时间 2021-7-29
     * @param $end string  //结束日期 2021-7-30
     * @param string $format //日期格式
     * @return array
     */
    public static function dateList($start, $end, $format = 'Y-m-d')
    {
        $start = strtotime($start);
        $end = strtotime($end);
        if (($end - $start) <= 86400) {
            return [date($format, $start)];
        }
        $list = [];
        for ($i = $start; $i <= $end; $i += 86400) {
            $list[] = date($format, $i);
        }
        return $list;
    }


    /**
     * 计算持续时长
     *
     * @param int $second 秒数
     * @return string $duration 5天10小时43分钟40秒
     */
    public static function secondduration($seconds)
    {
        $duration = '';

        $seconds = (int)$seconds;
        if ($seconds <= 0) {
            return $duration;
        }

        [$day, $hour, $minute, $second] = explode(' ', gmstrftime('%j %H %M %S', $seconds));

        $day -= 1;
        if ($day > 0) {
            $duration .= (int)$day . '天';
        }
        if ($hour > 0) {
            $duration .= (int)$hour . '小时';
        }
        if ($minute > 0) {
            $duration .= (int)$minute . '分钟';
        }
        if ($second > 0) {
            $duration .= (int)$second . '秒';
        }

        return $duration;
    }


    /**
     * 获取距离今日的年份
     * @param $birthday string //日期 //2010-07-10
     * @return false|int|mixed|string
     */
    public static function getYear(string $birthday)
    {
        $birthday = strtotime($birthday);

        [$year, $month, $day] = explode("-", date('Y-m-d', $birthday));
        $year_diff = date("Y") - $year;
        $month_diff = date("m") - $month;
        $day_diff = date("d") - $day;
        if ($day_diff < 0 && $month_diff < 0) {
            $year_diff--;
        }
        return $year_diff;
    }

    /**
     * 获取2个日期之间的月龄
     * @param $date1 //更早之前的日期  2010-7-1
     * @param $date2 //稍早之前的日期 2015-7-1
     * @param string $tags
     * @return float|int|mixed|string
     */
    public static function getMonthNum($date1, $date2, $tags = '-')
    {
        $d1 = explode($tags, $date1);
        $d2 = explode($tags, $date2);
        if (strtotime($date1) - strtotime($date2) > 0) {
            $monthsFromYear = abs($d1[0] - $d2[0]) * 12;
            $monthsFromMonth = $d1[1] - $d2[1];
            return $monthsFromYear + $monthsFromMonth;
        } else {
            $monthsFromYear = abs($d2[0] - $d1[0]) * 12;
            $monthsFromMonth = $d2[1] - $d1[1];
            return $monthsFromYear + $monthsFromMonth;
        }
    }

    /**
     * 获取距离指定日期之后N天的周几
     * @param $time
     * @param int $i
     * @return string
     */
    public static function getTimeWeek($time, $i = 0)
    {
        $weekarray = ["一", "二", "三", "四", "五", "六", "日"];
        $oneD = 24 * 60 * 60;
        $index = date("w", $time + $oneD * $i) == 0 ? 6 : date("w", $time + $oneD * $i) - 1;
        return "周" . $weekarray[$index];
    }

    /**
     * 获取本周所有日期
     * @param string $time
     * @param string $format
     * @return array
     */
    public static function get_week($time = '', $format = 'Y-m-d')
    {
        $time = $time != '' ? $time : time();
        //获取当前周几
        $week = date('w', $time);
        $date = [];
        for ($i = 1; $i <= 7; $i++) {
            $date[$i] = date($format, strtotime('+' . $i - $week . ' days', $time));
        }
        return $date;
    }

    /**
     * 获取最近七天所有日期
     * @param string $time
     * @param string $format
     * @return array
     */
    public static function get_weeks($time = '', $format = 'Y-m-d')
    {
        $time = $time != '' ? $time : time();
        //组合数据
        $date = [];
        for ($i = 1; $i <= 7; $i++) {
            $date[$i] = date($format, strtotime('+' . $i - 7 . ' days', $time));
        }
        return $date;
    }

    /**
     * 返回当前的毫秒时间戳
     * @return string
     */
    public static function msectime()
    {
        list($tmp1, $tmp2) = explode(' ', microtime());
        return sprintf('%.0f', ((float)$tmp1 + (float)$tmp2) * 1000);
    }

    /**
     * 获取指定年月的开始和结束时间戳
     * @param int $y 年份
     * @param int $m 月份
     * @return array(开始时间,结束时间)
     */
    public static function mStartAndEnd($y = 0, $m = 0)
    {
        $y = $y ? $y : date('Y');
        $m = $m ? $m : date('m');
        $d = date('t', strtotime($y . '-' . $m));
        return ["start" => strtotime($y . '-' . $m), "end" => mktime(23, 59, 59, $m, $d, $y)];
    }

    /**
     * excel时间转换
     * @param $date
     * @param bool $time
     * @return array|string
     */
    public static function excelTime($date, $time = false)
    {
        if (is_numeric($date)) {
            $jd = GregorianToJD(1, 1, 1970);
            $gregorian = JDToGregorian($jd + (int)$date - 25569);
            $date = explode('/', $gregorian);
            $date_str = str_pad($date[2], 4, '0', STR_PAD_LEFT)
                . "-" . str_pad($date[0], 2, '0', STR_PAD_LEFT)
                . "-" . str_pad($date[1], 2, '0', STR_PAD_LEFT)
                . ($time ? " 00:00:00" : '');
            return $date_str;
        }
        return $date;
    }




}